/**
 * $version:  0.1 
 * $Date: 2012-04-05 
 *
 * Copyright (C) 2010-2012 Jawa Software. All rights reserved.
 *
 */

package org.jawa.core;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.Serializable;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Date;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;

import org.apache.commons.beanutils.ConvertUtils;
import org.apache.commons.beanutils.converters.DateConverter;
import org.jawa.core.cache.CacheFactory;
import org.jawa.core.cluster.ClusterManager;
import org.jawa.core.container.PluginManager;
import org.jawa.core.database.DbConnectionManager;
import org.jawa.core.exception.InitializationException;
import org.jawa.core.log.Log;
import org.jawa.core.util.JawaGlobals;

public class JawaServer implements Serializable {

	private static final long serialVersionUID = 4875281305713873479L;

	// 服务器实例
	private static JawaServer instance;

	// 服务器名称
	private String name;

	// 服务器IP地址
	private String host;

	// 服务器版本
	private Version version;

	// 服务器最后一次启动时间
	private Date startDate;

	// 服务器是否初始化
	private boolean initialized = false;

	// 服务器是否已经启动
	private boolean started = false;

	// 是否已安装.
	private boolean setupMode = true;

	// 服务器信息.
	private JawaServerInfo serverInfo;

	// 服务器在集群中的唯一标识
	private String memberName;

	// 服务器工作目录
	private File jawaHome;

	// 服务器的 Class Loader
	private ClassLoader loader;

	// 在服务器启动或停止的时候发布事件.
	private List<JawaServerListener> listeners = new CopyOnWriteArrayList<JawaServerListener>();

	// 插件管理器.
	private PluginManager pluginManager;

	/**
	 * 返回服务器的单一实例.
	 * 
	 * @return 服务器的单一实例.
	 */
	public static JawaServer getInstance() {
		return instance;
	}

	/**
	 * 创建服务器并启动.
	 */
	public JawaServer() {
		if (instance != null) {
			throw new IllegalStateException("服务器已经启动");
		}

		instance = this;

		// 启动服务器
		start();
	}

	/**
	 * 初始化服务器.
	 * 
	 * @throws FileNotFoundException
	 */
	private void initialize() throws FileNotFoundException {

		// 确定服务器工作目录
		locateJawaHome();

		name = JawaGlobals.getXMLProperty("server.name", "JAWA").toLowerCase();

		try {
			host = InetAddress.getLocalHost().getHostName();
		} catch (UnknownHostException ex) {
			Log.warn("无法确定本地主机名.", ex);
		}

		version = new Version(1, 0, 0, Version.ReleaseStatus.Alpha);

		if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
			setupMode = false;
		}

		loader = Thread.currentThread().getContextClassLoader();

		// 初始化本地缓存
		try {
			CacheFactory.initialize();
		} catch (InitializationException e) {
			Log.error(e.getMessage(), e);
		}

		// 尝试启动集群缓存
		if (ClusterManager.isClusteringEnabled()) {
			ClusterManager.setClusteringEnabled(true);
		}

		DateConverter dateConverter = new DateConverter();
		dateConverter.setPattern("yyyy-MM-dd");
		ConvertUtils.register(dateConverter, java.util.Date.class);

		initialized = true;
	}

	/**
	 * 完成安装过程.
	 */
	public void finishSetup() {
		if (!setupMode) {
			return;
		}
		if ("true".equals(JawaGlobals.getXMLProperty("setup"))) {
			setupMode = false;
		}
	}

	/**
	 * 启动服务器.
	 */
	public void start() {
		try {
			initialize();

			startDate = new Date();

			serverInfo = new JawaServerInfo(name, host, version, startDate);

			// 初始化插件管理器,并不启动
			File pluginDir = new File(jawaHome, "plugins");
			pluginManager = new PluginManager(pluginDir);

			// 如果已正确安装
			if (!setupMode) {
				verifyDataSource();
			}

			// 启动插件管理器
			pluginManager.start();

			String startupBanner = "[服务器]  版本: " + version.getVersionString()
					+ " ( " + new Date() + " )";
			System.out.println(startupBanner);

			started = true;

		} catch (Exception e) {
			Log.error(e);
			System.out.println(("服务器启动失败"));
		}
	}

	/**
	 * 返回服务器主机名.
	 * 
	 * @return
	 */
	public String getHostName() {
		return host;
	}

	/**
	 * 校验服务器工作目录的正确性.
	 * 
	 * @param homeGuess
	 *            服务器工作目录.
	 * @param jawaConfigName
	 *            服务器配置文件.
	 * @return 服务器工作目录.
	 * @throws java.io.FileNotFoundException
	 *             服务器工作目录没有找到
	 */
	private File verifyHome(String homeGuess, String jawaConfigName)
			throws FileNotFoundException {
		File jawaHome = new File(homeGuess);
		File configFile = new File(jawaHome, jawaConfigName);
		if (!configFile.exists()) {
			throw new FileNotFoundException();
		} else {
			try {
				return new File(jawaHome.getCanonicalPath());
			} catch (Exception ex) {
				throw new FileNotFoundException();
			}
		}
	}

	/**
	 * <p>
	 * 返回当前服务器工作的目录.
	 * </p>
	 * 
	 * @throws FileNotFoundException
	 *             无法确定服务器工作的目录.
	 */
	private void locateJawaHome() throws FileNotFoundException {

		String jawaConfigName = "conf" + File.separator + "jawa.xml";

		if (JawaGlobals.getHomeDirectory() != null) {
			jawaConfigName = JawaGlobals.getConfigName();
			jawaHome = verifyHome(JawaGlobals.getHomeDirectory(),
					jawaConfigName);
		}
		if (jawaHome == null) {
			String homeProperty = System.getProperty("jawaHome");
			try {
				if (homeProperty != null) {
					jawaHome = verifyHome(homeProperty, jawaConfigName);
				}
			} catch (FileNotFoundException fe) {
				// 忽略.
			}
		}

		if (jawaHome == null) {
			try {
				jawaHome = verifyHome("..", jawaConfigName).getCanonicalFile();
			} catch (FileNotFoundException fe) {
				// 忽略.
			} catch (IOException ie) {
				// 忽略.
			}
		}

		if (jawaHome == null) {
			System.err.println("无法确定当前服务器的工作目录");
			throw new FileNotFoundException();
		} else {
			JawaGlobals.setHomeDirectory(jawaHome.toString());
			JawaGlobals.setConfigName(jawaConfigName);
		}
	}

	/**
	 * 验证数据库连接情况.
	 */
	private void verifyDataSource() {
		java.sql.Connection conn = null;
		try {
			conn = DbConnectionManager.getConnection();
			PreparedStatement stmt = conn
					.prepareStatement("SELECT count(*) FROM app_id");
			ResultSet rs = stmt.executeQuery();
			rs.next();
			rs.close();
			stmt.close();
		} catch (Exception e) {
			System.err.println("数据库安装或配置错误: " + "请检查数据库的配置是否正确,在 "
					+ "logs/error.log 文件中记录了详细的错误情况.");
			Log.error("数据库无法正常访问", e);
			throw new IllegalArgumentException(e);
		} finally {
			if (conn != null) {
				try {
					conn.close();
				} catch (SQLException e) {
					Log.error(e);
				}
			}
		}
	}

	/**
	 * 返回该服务器在集群中的唯一标识.
	 * 
	 * @return 服务器在集群中的唯一标识.
	 */
	public String getMemberName() {
		return memberName == null ? getHostName() : memberName;
	}

	/**
	 * 设置该服务器在集群中的唯一标识
	 * 
	 * @param 该服务器在集群中的唯一标识
	 *            .
	 */
	public void setMemberName(String memberName) {
		this.memberName = memberName;
	}

	/**
	 * 服务器是否已正确安装.
	 * 
	 * @return 服务器是否已正确安装.
	 */
	public boolean isSetupMode() {
		return setupMode;
	}

	/**
	 * 服务器是否已经开始启动.
	 * 
	 * @return 服务器是否已经开始启动.
	 */
	public boolean isStarted() {
		return started;
	}

	/**
	 * 返回服务器信息.
	 * 
	 * @return 服务器信息
	 */
	public JawaServerInfo getServerInfo() {
		if (!initialized) {
			throw new IllegalStateException("服务器没有初始化!");
		}
		return serverInfo;
	}

	/**
	 * 增加服务器事件监听.
	 * 
	 * @param jawaServerListener
	 */
	public void addServerListener(JawaServerListener jawaServerListener) {
		listeners.add(jawaServerListener);
	}

	/**
	 * 删除服务器事件监听.
	 * 
	 * @param 服务器事件
	 *            .
	 */
	public void removeServerListener(JawaServerListener listener) {
		listeners.remove(listener);
	}

	/**
	 * 服务器停止.
	 */
	public void stop() {
		ClusterManager.shutdown();

		// 停止所有插件
		if (pluginManager != null) {
			pluginManager.shutdown();
		}

		DbConnectionManager.destroyConnectionProvider();
		Log.info("服务器停止!");

	}

	/**
	 * Returns the <code>PluginManager</code> instance registered with this
	 * server.
	 * 
	 * @return the PluginManager instance.
	 */
	public PluginManager getPluginManager() {
		return pluginManager;
	}

	/**
	 * 返回服务器版本.
	 * 
	 * @return 服务器版本
	 */
	public Version getVersion() {
		return version;
	}

}
